home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 8: LINUX Games
/
Linux Cubed Series 8 - LINUX Games.iso
/
games
/
x11
/
strategy
/
xpat2-1.000
/
xpat2-1
/
xpat2-1.04
/
src
/
moves.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-10-13
|
22KB
|
816 lines
/*****************************************************************************/
/* */
/* */
/* X patience version 2 -- module moves.c */
/* */
/* General utility functions for all rule sets */
/* written by Heiko Eissfeldt and Michael Bischoff */
/* see COPYRIGHT.xpat2 for Copyright details */
/* */
/* */
/*****************************************************************************/
#include "xpatgame.h"
#include <time.h>
#include <limits.h> /* for INT_MAX */
#include <math.h>
/* RNG patch: */
int rng = 1;
static int myrand(int limit) {
/* select one of the RNGs. */
return rng ?
(int)(((double)(prand()) / (double)PRANDMAX) * (double)limit)
: prand() % limit;
}
static Move Klondike_deal_cards(void) {
int rem = CARDS_ON_PILE(IDECK);
if (rem > rules.param[2])
rem = rules.param[2];
graphics_pile_control(Disable, IDECK);
graphics_pile_control(Disable, VDECK);
if (rem) {
int i;
/* without graphics: */
for (i = 0; i < rem; ++i)
do_move(INDEX_OF_LAST_CARD(IDECK), VDECK);
} else {
while (!EMPTY(VDECK))
do_move(INDEX_OF_LAST_CARD(VDECK), IDECK);
++game.counter[1];
}
graphics_pile_control(EnableAndRedraw, IDECK);
graphics_pile_control(EnableAndRedraw, VDECK);
return NEW_CARDS_MOVE | MOVE(0, rem);
}
static void Klondike_undeal_cards(int num) {
graphics_pile_control(Disable, IDECK);
graphics_pile_control(Disable, VDECK);
if (!num) {
/* undo flip */
while (!EMPTY(IDECK))
do_move(INDEX_OF_LAST_CARD(IDECK), VDECK);
--game.counter[1];
} else {
int i;
for (i = 0; i < num; ++i)
do_move(INDEX_OF_LAST_CARD(VDECK), IDECK);
}
graphics_pile_control(EnableAndRedraw, IDECK);
graphics_pile_control(EnableAndRedraw, VDECK);
}
Move SlotShuffle(int fwd) {
int i, tab[MAXCARDS];
int num = INDEX_OF_FIRST_CARD(LAST_SLOT+1)
- INDEX_OF_FIRST_CARD(FIRST_SLOT);
Cardindex start = INDEX_OF_FIRST_CARD(FIRST_SLOT);
sprand(game.seed);
for (i = 0; i < num; ++i) {
int j;
j = myrand(num);
if (j < i) /* some correction */
j = i;
tab[i] = j;
}
if (fwd) {
for (i = 0; i < num; ++i)
if (tab[i] != i) {
/* swap card */
Card c;
c = game.cards[start+i];
game.cards[start+i] = game.cards[start+tab[i]];
game.cards[start+tab[i]] = c;
}
} else {
for (i = num-1; i >= 0; --i)
if (tab[i] != i) {
/* swap card */
Card c;
c = game.cards[start+i];
game.cards[start+i] = game.cards[start+tab[i]];
game.cards[start+tab[i]] = c;
}
}
for (i = FIRST_SLOT; i <= LAST_SLOT; ++i)
draw_pileupdate(i, 0);
return SHUFFLING;
}
#ifdef DEBUG
static int type[200];
static void count(void) {
int i;
memset(type, 0, sizeof(type));
for (i = 0; i < rules.numcards; ++i)
++type[game.cards[i]];
for (i = 0; i < 52; ++i)
if (type[i] != rules.numdecks)
fprintf(stderr, "Card %d: Number = %d\n", i, type[i]);
}
#endif
void memo_alloc(int num) {
game.numalloc = ((num - 1) | 0xf) + 1; /* multiple of 16 */
if (!(game.move = malloc(game.numalloc * sizeof(Move)))) {
fprintf(stderr, "out of memory\n");
exit(EXIT_FAILURE);
}
}
static void makespace(int n) {
if (game.move_ptr + n > game.numalloc) {
Move *tmpnew;
if (!(tmpnew = realloc(game.move, (game.numalloc+256) * sizeof(Move)))) {
fprintf(stderr, "out of memory. saving game in xpat.tmp\n");
save_game("xpat.tmp");
exit(EXIT_FAILURE);
}
game.numalloc += 256;
game.move = tmpnew;
}
}
/* A new move is done. This resets the limit of redo to the current point. */
/* Also, the Bookmark is possibly taken back */
/* if the AUTOFILL_TMPS feature is set, empty tmps are filled from the deck */
void store_move(Move m) {
makespace(1);
if (game.bookmark > game.move_ptr) /* reset Bookmark position */
game.bookmark = game.move_ptr;
if ((rules.variant & AUTOFILL_TMPS) && (m & SPECIAL_MOVE) != SPECIAL_MOVE) {
Pileindex srcpile = DSTPILE(m >> 16);
if (game.piletype[srcpile] == Tmp && (!EMPTY(IDECK) || !EMPTY(VDECK))) {
/* this is the case we have to deal with */
makespace(3);
game.move[game.move_ptr++] = COMPOUND_BEGIN;
game.move[game.move_ptr++] = m;
game.move[game.move_ptr++] = !EMPTY(VDECK) ?
do_move(INDEX_OF_LAST_CARD(VDECK), srcpile) :
do_move(INDEX_OF_LAST_CARD(IDECK), srcpile);
m = COMPOUND_END;
}
}
game.move[game.move_ptr] = m;
game.stored_moves = ++game.move_ptr;
++game.n_moves;
/* now that the work is done, check for finished game */
check_win_game();
}
static void undo_give_cards(int num) {
/* num = number of cards */
/* printf("undo give %d cards\n", num); */
if (rules.undeal_cards)
(*rules.undeal_cards)(num);
else if (rules.variant & KLONDIKE_DEAL)
Klondike_undeal_cards(num);
else {
int i;
graphics_pile_control(Disable, IDECK);
for (i = FIRST_SLOT + num; i != FIRST_SLOT; --i)
/* slot i-1 back to deck */
do_move(game.ind[i]-1, IDECK);
graphics_pile_control(EnableAndRedraw, IDECK);
}
}
int undo_move(void) {
Move m;
int retval = 1; /* no cheat */
if (!game.move_ptr) {
/* possibly undo restart game? */
if (game.stored_moves) {
jumpto_movenr(game.stored_moves); /* must replay the game */
return 1;
}
return 0;
}
m = game.move[--game.move_ptr];
--game.n_moves;
if ((m & SPECIAL_MOVE) == SPECIAL_MOVE) {
switch (m & SPECIAL_MASK) {
case COMPOUND_END:
{
int remember_count, remgraphic;
if ((remgraphic = game.graphic))
graphics_control(Disable);
/* undo multiple moves */
remember_count = game.n_moves;
while (game.move[game.move_ptr-1] != COMPOUND_BEGIN) {
if (undo_move() == 2)
retval = 2;
}
--game.move_ptr;
game.n_moves = remember_count;
if (remgraphic)
graphics_control(EnableAndRedraw);
}
return retval;
case ADD_CHEAT:
game.cheat_count += (int)(m & SPECIAL_ARGS);
return 2;
case NEW_CARDS_MOVE:
undo_give_cards(DSTPILE(m));
if (DSTPILE(m)) {
game.cheat_count += DSTPILE(m);
return 2;
}
return 1;
case ROTATE_UP:
/* change counter */
{ int c;
for (c = 0; c < 4; ++c)
if (rules.paramstring[c] == TXTI_ROTATE)
game.counter[c] -= 2;
}
RotateDown(SRCIND(m));
return 1;
case ROTATE_DOWN:
/* change counter */
{ int c;
for (c = 0; c < 4; ++c)
if (rules.paramstring[c] == TXTI_ROTATE)
game.counter[c] -= 2;
}
RotateUp(SRCIND(m));
return 1;
case SHUFFLING:
SlotShuffle(0);
return 1;
default:
/* printf("Move in error is %08lx\n", m); */
assert(0);
}
return 0;
}
/* standard move follows */
if (m & MOVE_TURNED) {
m >>= 16; /* upper part = undo information */
++game.cheat_count;
game.visible[game.ind[DSTPILE(m)+1]-1] = 0; /* invisible again! */
retval = 2;
} else
m >>= 16; /* upper part = undo information */
(void)do_move(SRCIND(m), DSTPILE(m));
if (retval == 2)
draw_pileupdate(DSTPILE(m), 0); /* hide card again! */
return retval;
}
int redo_move(void) {
Move m;
int retval = 1;
if (game.move_ptr == game.stored_moves)
return 0; /* no redo possible */
m = game.move[game.move_ptr++];
++game.n_moves;
if ((m & SPECIAL_MOVE) == SPECIAL_MOVE) {
switch (m & SPECIAL_MASK) {
case COMPOUND_BEGIN:
{
int remember_count, remgraphic;
if ((remgraphic = game.graphic))
graphics_control(Disable);
/* redo multiple moves */
remember_count = game.n_moves;
while (game.move[game.move_ptr] != COMPOUND_END) {
if (redo_move() == 2)
retval = 2;
}
++game.move_ptr;
game.n_moves = remember_count;
if (remgraphic)
graphics_control(EnableAndRedraw);
}
return retval;
case ADD_CHEAT:
game.cheat_count -= (int)(m & SPECIAL_ARGS);
return 2;
case ROTATE_UP:
RotateUp(SRCIND(m));
return 1;
case ROTATE_DOWN:
RotateDown(SRCIND(m));
return 1;
case NEW_CARDS_MOVE:
retval = DSTPILE(give_new_cards());
if (retval) {
game.cheat_count -= retval;
return 2;
}
return 1;
case SHUFFLING:
SlotShuffle(1);
return 1;
default:
assert(0);
}
}
if (m & MOVE_TURNED) {
--game.cheat_count;
retval = 2;
}
(void)do_move(SRCIND(m), DSTPILE(m));
return retval;
}
static void shuffle(void) {
int i, d, v, c;
int tmp[MAXCARDS];
if (game.graphic)
show_message(" ");
i = 0;
for (d = 0; d < rules.numdecks; ++d)
for (v = 0; v < rules.cards_per_color; ++v)
for (c = 0; c < 4; ++c)
tmp[i++] = c + (v << 2);
assert(i <= rules.numcards);
c = 0;
while (i < rules.numcards)
tmp[i++] = JOKER + c++;
while (c < rules.numjokers)
tmp[myrand(rules.numcards)] = JOKER + c++; /* subst old cards */
for (i = rules.numcards; i; --i) {
v = myrand(i);
/* printf("rand() = %d\n", v); */
/* look for the vth non-empty card in tmp */
c = -1;
do {
while (tmp[++c] == -1)
; /* skip card */
} while (v--);
game.cards[i-1] = tmp[c];
/* printf("c = %d, card = %d\n", c, game.cards[i-1]); */
tmp[c] = -1;
}
}
Pileindex getpile(Cardindex ind) {
int i;
assert(ind < rules.numcards);
i = 0;
while (ind >= game.ind[i+1])
++i;
return i;
}
/* These are the central card moving routines, i.e. the only functions which */
/* change the contents of the game.cards array within a game. */
/* Every time anything is changed, one of these functions is called. */
/* We can reset hint tables at this point. */
Move RotateUp(Cardindex src) {
Pileindex p = getpile(src);
int c;
Cardindex i;
cmd_CancelSelection();
i = INDEX_OF_LAST_CARD(p);
c = game.cards[i];
while (--i >= src)
game.cards[i+1] = game.cards[i];
game.cards[i+1] = c;
draw_pileupdate(p, 0);
/* change counter */
for (c = 0; c < 4; ++c)
if (rules.paramstring[c] == TXTI_ROTATE)
++game.counter[c];
return ROTATE_UP | MOVE(src, 0);
}
Move RotateDown(Cardindex src) {
Pileindex p = getpile(src);
int c;
Cardindex i;
cmd_CancelSelection();
c = game.cards[src];
for (i = src; i < INDEX_OF_LAST_CARD(p); ++i)
game.cards[i] = game.cards[i+1];
game.cards[i] = c;
draw_pileupdate(p, 0);
/* change counter */
for (c = 0; c < 4; ++c)
if (rules.paramstring[c] == TXTI_ROTATE)
++game.counter[c];
return ROTATE_DOWN | MOVE(src, 0);
}
Move do_move(Cardindex srcindex, Pileindex dstpile) {
int srcpile, numcards;
int tmp[MAXCARDS];
int vis;
Move m;
cmd_CancelSelection(); /* calls cmd_ResetHints() */
if (srcindex == -1)
return NO_MOVE;
srcpile = getpile(srcindex);
numcards = game.ind[srcpile+1] - srcindex;
m = MOVE(srcindex, dstpile);
if (srcindex > game.ind[srcpile] && !game.visible[srcindex-1] &&
(srcpile != IDECK || rules.variant & DECK_VISIBLE)) {
game.visible[srcindex-1] = 1;
m |= MOVE_TURNED;
}
memcpy(tmp, game.cards+srcindex, numcards * sizeof(int));
/* moved cards are normally visible */
vis = dstpile != IDECK || rules.variant & DECK_VISIBLE;
/* printf("move %d to %d, %d cards\n", srcindex, dstpile, numcards); */
assert(srcpile != dstpile);
if (srcpile < dstpile) {
/* ldir */
int i;
m |= MOVE(game.ind[dstpile+1]-numcards, srcpile) << 16;
for (i = srcindex; i < game.ind[dstpile+1]-numcards; ++i)
move_card_data(i, i+numcards);
memcpy(game.cards+i, tmp, numcards * sizeof(int));
for (; i < game.ind[dstpile+1]; ++i)
game.visible[i] = vis;
for (i = srcpile + 1; i <= dstpile; ++i)
game.ind[i] -= numcards;
} else {
/* lddr ; shift a block backwards; begin at the tail */
int i;
m |= MOVE(game.ind[dstpile+1], srcpile) << 16;
for (i = srcindex - 1; i >= game.ind[dstpile+1]; --i)
move_card_data(i+numcards, i);
memcpy(game.cards + game.ind[dstpile+1], tmp, numcards * sizeof(int));
for (i = 0; i < numcards; ++i)
game.visible[game.ind[dstpile+1] + i] = vis;
for (i = dstpile+1; i <= srcpile; ++i)
game.ind[i] += numcards;
}
#ifdef DEBUG
count();
#endif
/* inform graphics interface of change */
draw_pileupdate(srcpile, -numcards);
draw_pileupdate(dstpile, numcards);
/* printf("Move done is %08lx\n", m); */
return m;
}
int all_to_stack(void) {
int i, flag, anymove;
anymove = 0;
do {
flag = 0;
for (i = 0; i < game.numpiles; ++i) {
switch (game.piletype[i]) {
case Slot:
case Tmp:
case FaceupDeck:
if (move_to_stack(i)) {
flag = 1;
anymove = 1;
}
break;
default:
;
}
}
} while (flag);
return anymove;
}
static void init_seed(long seed) {
if (seed < 0) {
game.seed = -1L; /* to guarantee a mismatch later */
seed = (long)time(NULL);
}
seed %= PRANDMAX;
if (seed < 0L)
seed += PRANDMAX; /* I think this shouldn't happen */
if (seed == game.seed) { /* restart game */
/* stored_moves stays valid */
game.cheat_count += 1000;
} else {
game.seed = seed;
game.cheat_count = game.stored_moves = 0;
game.finished = False; /* this is really a new game */
}
game.n_moves = 0;
game.move_ptr = 0;
game.counter[0] = 0;
game.counter[1] = 0;
game.counter[2] = 0;
game.counter[3] = 0;
sprand(seed);
}
static void distribute(char *xx, int rest, int piles)
{ int i, k, invert = 0;
if (!piles)
return;
if (2 * rest > piles+1) {
rest = piles - rest;
invert = 1;
}
memset(xx, invert, piles);
if (rest) {
k = (piles-1) / rest + 1; /* ceil(slots/rest) */
for (i = 0; i < piles; i += k) {
xx[i] = 1-invert;
if (!--rest)
break;
}
/* the rest is distributed quite ugly */
for (i = 0; rest; ++i)
if (xx[i] == invert) {
xx[i] = 1-invert;
--rest;
}
}
}
static void gen_newgame(void)
{ int i, rest;
char xx[MAXPILES];
memset(xx, 0, MAXPILES);
if (rules.facedown && !rules.faceup) {
fprintf(stderr, "newgame(): topmost card must be face-up, correcting it\n");
--rules.facedown;
++rules.faceup;
}
if (rules.newgame_bits & SLOTS_SAME)
rest = 0;
else
rest = rules.numcards % rules.numslots; /* cards that are too much */
if (rules.numcards < rest + rules.numslots * (rules.faceup + rules.facedown)) {
fprintf(stderr, "newgame(): too many cards specified, resetting to min values\n");
rules.faceup = 1;
rules.facedown = 0;
}
/* generate nice distribution of rest cards */
if (rest > rules.numtmps || (rules.newgame_bits & FORCE_SLOTS))
/* distribute on slots */
distribute(xx+FIRST_SLOT, rest, rules.numslots);
else
/* distribute on tmps */
distribute(xx+LAST_SLOT+1, rest, rules.numtmps);
for (i = 0; i <= FIRST_SLOT; ++i)
game.ind[i] = 0;
for (i = FIRST_SLOT; i <= LAST_SLOT; ++i) {
int j;
game.ind[i+1] = game.ind[i] + rules.facedown + rules.faceup + xx[i];
for (j = game.ind[i+1] - rules.faceup; j < game.ind[i+1]; ++j)
game.visible[j] = 1; /* card is turned */
}
while (++i < game.numpiles)
game.ind[i] = game.ind[i-1] + xx[i-1];
/* all cards on the tmps are faceup */
for (i = game.ind[LAST_SLOT+1]; i < game.ind[IDECK]; ++i)
game.visible[i] = 1;
if (rules.variant & DECK_VISIBLE)
game.visible[rules.numcards-1] = 1; /* topmost card on deck */
if (!rules.facedown)
for (i = game.ind[FIRST_SLOT]; i < game.ind[LAST_SLOT+1]; ++i)
game.visible[i] = 1; /* all cards on the slots visible */
}
static void MO_newgame(void) {
int i, remcards = rules.numcards;
/* specific part: */
for (i = 0; i < rules.numslots; ++i) {
int here;
here = min(remcards, rules.faceup + rules.facedown);
remcards -= here;
game.ind[rules.numstacks+i+1] = game.ind[rules.numstacks+i] + here;
if (here) {
Cardindex j;
j = game.ind[rules.numstacks+i+1] - 1;
do
game.visible[j--] = 1;
while (j >= game.ind[rules.numstacks+i] + rules.facedown);
}
}
}
void newgame(long seed) {
int i;
game.randomgame = (seed == -1L);
game.savecount = 0;
game.bookmark = 0;
init_seed(seed);
shuffle(); /* initialize game.cards */
for (i = 0; i < rules.numcards; ++i)
game.visible[i] = 0;
for (i = 0; i < game.numpiles; ++i)
game.ind[i] = 0; /* no cards on the piles */
game.ind[i] = rules.numcards; /* rest on the deck */
if (rules.new_game)
(*rules.new_game)();
else if (rules.newgame_bits & SEQUENTIAL)
MO_newgame();
else
gen_newgame();
if (game.graphic)
for (i = 0; i < game.numpiles; ++i)
pile_resize(i);
/* (void)do_move(-1, -1); */ /* reset game.*/
cmd_ResetHints();
game.srcind = UNSELECTED;
}
/* generic give-cards routine */
Move give_new_cards(void) {
if (rules.deal_cards)
return (*rules.deal_cards)();
else if (rules.variant & KLONDIKE_DEAL)
return Klondike_deal_cards();
else {
int i, rem;
graphics_pile_control(Disable, IDECK);
rem = CARDS_ON_PILE(IDECK);
if (rem > rules.numslots)
rem = rules.numslots;
for (i = 0; i < rem; ++i)
do_move(INDEX_OF_LAST_CARD(IDECK), rules.numstacks + i);
graphics_pile_control(EnableAndRedraw, IDECK);
return NEW_CARDS_MOVE | MOVE(0, rem);
}
}
extern struct rules Spider_rules, Gypsy_rules, Klondike_rules, modSpider_rules;
extern struct rules Seahaven_rules, FreeCell_rules, IdiotsDelight_rules,
MonteCarlo_rules, MidnightOil_rules, Calculation_rules, Michaels_rules,
Canfield_rules, modCanfield_rules, Royal_rules, Bakers_rules;
struct game game;
struct rules rules;
struct rules *rulepool[] = {
&Spider_rules,
&Gypsy_rules,
&Klondike_rules,
&Seahaven_rules,
&FreeCell_rules,
&IdiotsDelight_rules,
&MonteCarlo_rules,
&MidnightOil_rules,
&Calculation_rules,
&modCanfield_rules,
&Michaels_rules,
&Canfield_rules,
&Royal_rules,
&Bakers_rules,
NULL
};
struct rules *getrules(const char *ruleset) {
int i;
for (i = 0; rulepool[i]; ++i)
if (!strcmp(ruleset, rulepool[i]->shortname) ||
(rulepool[i]->abbrev && !strcmp(ruleset, rulepool[i]->abbrev))) {
return rulepool[i];
}
return NULL;
}
void new_rules(const char *ruleset, int decks, int slots, int faceup, int facedown,
int jokers, int tmps, int param0, int param1, int param2, int param3) {
int i;
struct rules *rp;
if (!game.numalloc)
memo_alloc(512);
game.seed = -1L; /* no replay is valid */
/* that will set finished to False */
if (!(rp = getrules(ruleset))) {
fprintf(stderr, "Unknown rule set. Known rules are:\n");
for (i = 0; rulepool[i]; ++i) {
char buff[20];
if (rulepool[i]->abbrev)
sprintf(buff, "(Abbrev %-2s)", rulepool[i]->abbrev);
else
strcpy(buff, " ");
fprintf(stderr, "%-15s %s -- %s\n", rulepool[i]->shortname, buff,
rulepool[i]->longname ? rulepool[i]->longname : "");
}
exit(EXIT_FAILURE);
}
rules = *rp; /* copy std game.*/
/* rule customization: */
if (!(rules.customizable & CUSTOM_DECKS )) decks = -1;
if (!(rules.customizable & CUSTOM_SLOTS )) slots = -1;
if (!(rules.customizable & CUSTOM_JOKERS)) jokers = -1;
if (!(rules.customizable & CUSTOM_TMPS )) tmps = -1;
if (!(rules.customizable & CUSTOM_FACEUP)) faceup = -1;
if (!(rules.customizable & CUSTOM_FACEDOWN)) facedown = -1;
if (!(rules.customizable & CUSTOM_PARAM0)) param0 = -1;
if (!(rules.customizable & CUSTOM_PARAM1)) param1 = -1;
if (!(rules.customizable & CUSTOM_PARAM2)) param2 = -1;
if (!(rules.customizable & CUSTOM_PARAM3)) param3 = -1;
if (jokers >= 0 && jokers != rules.numjokers) {
rules.numjokers = jokers;
rules.customized |= CUSTOM_JOKERS;
rules.maxscore = 0; /* custom: maxscore unknown */
}
if (param0 >= 0 && param0 != rules.param[0]) {
rules.param[0] = param0;
rules.customized |= CUSTOM_PARAM0;
rules.maxscore = 0; /* custom: maxscore unknown */
}
if (param1 >= 0 && param1 != rules.param[1]) {
rules.param[1] = param1;
rules.customized |= CUSTOM_PARAM1;
rules.maxscore = 0; /* custom: maxscore unknown */
}
if (param2 > 0 && param2 != rules.param[2]) {
rules.param[2] = param2;
rules.customized |= CUSTOM_PARAM2;
rules.maxscore = 0; /* custom: maxscore unknown */
}
if (param3 >= 0 && param3 != rules.param[3]) {
rules.param[3] = param3;
rules.customized |= CUSTOM_PARAM3;
}
if (faceup >= 0 && faceup != rules.faceup) {
rules.faceup = faceup;
rules.customized |= CUSTOM_FACEUP;
rules.maxscore = 0; /* custom: maxscore unknown */
}
if (facedown >= 0 && facedown != rules.facedown) {
rules.facedown = facedown;
rules.customized |= CUSTOM_FACEDOWN;
rules.maxscore = 0; /* custom: maxscore unknown */
}
if (slots > 0 && slots != rules.numslots) {
rules.numslots = slots;
rules.customized |= CUSTOM_SLOTS;
rules.maxscore = 0; /* custom: maxscore unknown */
}
if (decks > 0 && decks != rules.numdecks) {
rules.numdecks = decks;
rules.customized |= CUSTOM_DECKS;
rules.maxscore = 0; /* custom: maxscore unknown */
}
if (tmps >= 0 && tmps != rules.numtmps) {
rules.numtmps = tmps;
rules.customized |= CUSTOM_TMPS;
}
if (rules.numstacks >= 4)
rules.numstacks = rules.numdecks * 4;
rules.numcards = rules.cards_per_color * 4 * rules.numdecks + rules.numjokers;
if (rules.numcards > MAXCARDS) {
fprintf(stderr, "new_rules(): parameters give too many cards\n");
exit(EXIT_FAILURE);
}
if (rules.numslots + rules.numstacks + 1 > MAXPILES) {
fprintf(stderr, "new_rules(): parameters give too many slots\n");
exit(EXIT_FAILURE);
}
for (i = 0; i < rules.numstacks; ++i)
game.piletype[i] = Stack;
for (; i < rules.numstacks + rules.numslots; ++i)
game.piletype[i] = Slot;
for (; i < rules.numstacks + rules.numslots + rules.numtmps; ++i)
game.piletype[i] = Tmp;
if (rules.variant & DECK_SOURCE)
game.piletype[i++] = FaceupDeck;
game.piletype[i++] = FacedownDeck;
game.numpiles = i;
for (i = 0; i <= game.numpiles; ++i)/* this fixes coredump-bug */
game.ind[i] = 0; /* no card currently there */
if (rules.initfunc) /* initrules-hook */
(*rules.initfunc)();
rp->inited = 1;
}